package cn.finedo.service;

import cn.finedo.Enum.PromptWordConstant;
import cn.finedo.common.NonUtil;
import cn.finedo.common.ReturnValueDomain;
import cn.finedo.dto.*;
import cn.finedo.entity.PromptEntity;
import cn.finedo.mapper.AILLMMapper;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import okhttp3.*;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.OutputStream;
import java.io.Reader;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.concurrent.TimeUnit;

@Service
public class AILLMService {

    // 日志打印
    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    // 大模型地址
    @Value("${llmconfig.llmurl}")
    private String llmurl;

    @Value("${llmconfig.llmmaxlength}")
    private String llmmaxlength;

    @Value("${llmconfig.temperature}")
    private String llmtemperature;

    @Value("${llmconfig.topp}")
    private String llmtopp;
    // 设置连接超时、读取超时和写入超时（以毫秒为单位）
    @Value("${llmconfig.connecttimeoutmillis}")
    private int connectTimeoutMillis;
    @Value("${llmconfig.readtimeoutmillis}")
    private int readTimeoutMillis;
    @Value("${llmconfig.writetimeoutmillis}")
    private int writeTimeoutMillis;

    final static String STATUS_CODE_400_MSG = "请检查参数是否为空，以及参数名称、参数个数、参数类型是否正确。";
    final static String STATUS_CODE_500_MSG = "服务内部异常";

    private OkHttpClient client = new OkHttpClient.Builder()
            .connectTimeout(connectTimeoutMillis, TimeUnit.MILLISECONDS)
            .readTimeout(readTimeoutMillis, TimeUnit.MILLISECONDS)
            .writeTimeout(writeTimeoutMillis, TimeUnit.MILLISECONDS)
            .build();

    @Autowired
    private AILLMMapper aillmMapper;

    /**
     * 查询提示词
     * @param promptDto
     * @return
     */
    public ReturnValueDomain<PromptEntity> queryPrompt(PromptDto promptDto) {
        ReturnValueDomain<PromptEntity> ret = new ReturnValueDomain<>();
        LambdaQueryWrapper<PromptEntity> qw = new LambdaQueryWrapper<>();
        qw.eq(PromptEntity::getSenceid, promptDto.getSenceid());
        PromptEntity entity = null;
        try {
            entity = aillmMapper.selectOne(qw);
        } catch (Exception e) {
            logger.error("查询提示词失败！！！", e);
            return ret.setFail("查询提示词失败");
        }
        return ret.setSuccess("查询提示词成功", entity);
    }

    /**
     * 构建请求体
     * @param dto
     * @return
     */
    private RequestBody prepareParam(LLMRequestDto dto) {
        //查询提示词
        ReturnValueDomain<PromptEntity> ret = queryPrompt(new PromptDto(dto.getSenceid()));
        if (NonUtil.isNon(ret) || ret.isFail() || NonUtil.isNon(ret.getObject())) {
            logger.error("查询提示词失败！！！");
            return null;
        }
        PromptEntity entity = ret.getObject();
        String prePrompt = entity.getPrompttext();
        String inputText = dto.getInputtext();
        String finalPromptText = prePrompt.replace("[这里插入要输入的文本]", inputText);
        LLMDto llmDto = new LLMDto();
        logger.info("开始判断是否为流式: " + dto.getUsestream());
        if (dto.getUsestream() != null && dto.getUsestream() == 1) {
            llmDto.setStream(1);
        }
        LLMDto paramLLMDto = dto.getLlmdto();
        if (NonUtil.isNon(paramLLMDto)) {
            llmDto.setMaxlength(Integer.parseInt(llmmaxlength));
            llmDto.setTemperature(Float.parseFloat(llmtemperature));
            llmDto.setTopp(Float.parseFloat(llmtopp));
        } else {
            llmDto.setMaxlength(NonUtil.isNon(paramLLMDto.getMaxlength()) ? Integer.parseInt(llmmaxlength) : paramLLMDto.getMaxlength());
            llmDto.setTemperature(NonUtil.isNon(paramLLMDto.getTemperature()) ? Float.parseFloat(llmtemperature) : paramLLMDto.getTemperature());
            llmDto.setTopp(NonUtil.isNon(paramLLMDto.getTopp()) ? Float.parseFloat(llmtopp) : paramLLMDto.getTopp());
        }
        llmDto.setPrompt(finalPromptText);
        llmDto.setHistory(new ArrayList<>());
        RequestBody body = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), JSON.toJSONString(llmDto));
        return body;
    }


    /**
     * 提示词sse推送
     * @param dto
     * @param outputStream
     * @return
     */
    public void streamSseEvent(LLMRequestDto dto, OutputStream outputStream) {
        if (NonUtil.isNon(dto) || NonUtil.isNon(dto.getInputtext())) {
            throw new RuntimeException("请输入内容");
        }
        // 构建请求体
        RequestBody body = prepareParam(dto);
        if (NonUtil.isNon(body)) {
            throw new RuntimeException("未配置的提示词");
        }
        // 构建请求
        Request request = new Request.Builder()
                .url(llmurl)
                .post(body)
                .build();
        try {
            try (Response httpResponse = client.newCall(request).execute()) {
                if (!httpResponse.isSuccessful()) {
                    throw new RuntimeException("unExcepted code" + httpResponse);
                }
                // 发送流到客户端
                try (Reader reader = httpResponse.body().charStream();
                     BufferedReader bufferedReader = new BufferedReader(reader)) {
                    String line;
                    while ((line = bufferedReader.readLine()) != null) {
                        outputStream.write((line + "\n").getBytes(StandardCharsets.UTF_8));
                        outputStream.flush();
                    }
                }
            } catch (IOException e) {
                logger.error("处理HTTP响应或写入输出流时出错", e);
                throw new RuntimeException("处理HTTP响应或写入输出流时出错", e);
            }
        } catch (RuntimeException e) {
            logger.error("SSE推送过程中发生运行时异常", e);
        } finally {
            try {
                if (outputStream != null) {
                    outputStream.close();
                }
            } catch (IOException e) {
                logger.error("关闭输出流失败", e);
            }
        }
    }

    public LLMResDto sendEvent(LLMRequestDto dto) throws CustomException {
        RequestBody body = prepareRequestAndValidate(dto);
        // 构建请求
        Request request = new Request.Builder()
                .url(llmurl)
                .post(body)
                .build();
        try (Response httpResponse = client.newCall(request).execute()) {
            if (!httpResponse.isSuccessful()) {
                throw new CustomException("500", STATUS_CODE_500_MSG, 2);
            }
            // 读取大模型返回的内容
            String responseBody = httpResponse.body().string();
            String senceid = dto.getSenceid();
            String handlerRes = processResponseBody(senceid, dto, responseBody);
            // 构造JSON对象
            LLMResDto llmResDto = new LLMResDto();
            llmResDto.setData(handlerRes);
            return llmResDto;
        } catch (IOException e) {
            logger.error("请求大模型服务失败或写入输出流时出错", e);
            throw new CustomException("500", STATUS_CODE_500_MSG, 2);
        }

    }


    private RequestBody prepareRequestAndValidate(LLMRequestDto dto) throws CustomException {
        if (dto == null || dto.getInputtext() == null) {
            throw new CustomException("400", STATUS_CODE_400_MSG, 1);
        }
        // 构建请求体
        RequestBody body = prepareParam(dto);
        if (body == null) {
            throw new CustomException("400", "未配置或未查询到提示词", 1);
        }
        return body;
    }

    private static boolean parseTemplates(String userInput) {
        String templateString = PromptWordConstant.PromptWords;
        String[] list = templateString.split("\\|");
        for (String template : list) {
            // 将模板按 "&" 分割为多个条件
            String[] conditions = template.split("&");
            boolean match = true;

            // 检查用户对话是否满足所有条件
            for (String condition : conditions) {
                if (!userInput.contains(condition.trim())) {
                    match = false;
                    break;
                }
            }

            // 如果满足任意一组模板数据，返回 true
            if (match) {
                return true;
            }
        }

        // 未匹配任何模板数据组
        return false;
        // 按 | 分割字符串
    }

    public String processResponseBody(String senceid, LLMRequestDto dto, String responseBody) throws CustomException {
        String cleanedRes = null;
        if (NonUtil.isNotNon(responseBody)) {
            logger.info("大模型返回内容:" + responseBody);
            try {
                responseBody = responseBody.replaceAll("data: ", "");
                // 去掉 \r\n
                String noNewlinesInput = responseBody.replaceAll("\\r|\\n", "");
                // 精确去除 ping-YYYY-MM-DD HH:MM:SS.ssssss 格式的时间戳
                cleanedRes = noNewlinesInput.replaceAll(": ping - \\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}\\.\\d+", "");
                logger.info("解析后的大模型内容:" + cleanedRes);
                if ("LLM36622024070000024".equals(senceid)) {
                    return cleanedRes.replaceAll("固定文本", "关键提示");
                }
                else if (StringUtils.equals(senceid, "LLM28582024050000019")) {
                    String[] strings = new String[]{
                            PromptWordConstant.ChannelName1,
                            PromptWordConstant.ChannelName2,
                            PromptWordConstant.ChannelName3,
                            PromptWordConstant.ChannelName41,
                            PromptWordConstant.ChannelName42,
                            PromptWordConstant.ChannelName43,
                            PromptWordConstant.ChannelName44,
                            PromptWordConstant.ChannelName45,
                            PromptWordConstant.ChannelName46,
                            PromptWordConstant.ChannelName47,
                            PromptWordConstant.ChannelName5,
                            PromptWordConstant.ChannelName6,
                    };
                    boolean channelFlag = false;

                    if (StringUtils.isNotBlank(dto.getChannel())) {
                        channelFlag = Arrays.asList(strings).contains(dto.getChannel());
                    }
                    // 判断是否是投诉工单渠道

                    // 设置senceid 为渠道Id
                    if (channelFlag) {
                        dto.setSenceid(dto.getChannel());
                    }

                    // 初始化返回值
                    String cleanedRes1;

                    // 先查询请求提示词模板
                    // 2.返回调用大模型的请求体
                    RequestBody requestBody = prepareRequestAndValidate(dto);
                    if (NonUtil.isNon(requestBody)){
                        throw new CustomException("500", "渠道模板获取失败", 2);
                    }

                    // 3.构建请求
                    Request request = new Request.Builder()
                            .url(llmurl)
                            .post(requestBody)
                            .build();

                    // 4.发送请求
                    try (Response httpResponse = client.newCall(request).execute()) {
                        if (!httpResponse.isSuccessful()) {
                            throw new CustomException("500", STATUS_CODE_500_MSG, 2);
                        }
                        // 5.读取大模型返回的内容
                        String resBody = null;
                        if (httpResponse.body() != null) {
                            resBody = httpResponse.body().string();
                        }
                        if (resBody == null) {
                            throw new CustomException("500", STATUS_CODE_500_MSG, 2);
                        }

                        resBody = resBody.replaceAll("data: ", "");

                        // 去掉 \r\n
                        String newlinesInput = resBody.replaceAll("\\r|\\n", "");
                        // 精确去除 ping-YYYY-MM-DD HH:MM:SS.ssssss 格式的时间戳
                        cleanedRes1 = newlinesInput.replaceAll(": ping - \\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}\\.\\d+", "");
                        // 构造JSON对象
                    } catch (IOException e) {
                        logger.error("请求大模型服务失败或写入输出流时出错", e);
                        throw new CustomException("500", STATUS_CODE_500_MSG, 2);
                    }

                    String addStr = "(已告知处理仅此一次)";

                    // 先判断用户对话中是否包含，模板提示词
                    boolean sensitiveWordsFlag = parseTemplates(dto.getInputtext());
                    // 在渠道内
                    // 在渠道内
                    if (channelFlag) {
                        // 渠道1
                        if (StringUtils.equals(PromptWordConstant.ChannelName1, dto.getChannel())) {
                            // 添加专项说明
                            cleanedRes1 = cleanedRes1 + " 5.是否解决: 问题已解决，用户认可处理结果。";

                            // 判断是否包含敏感词
                            if (sensitiveWordsFlag) {
                                cleanedRes1 = cleanedRes1.replaceAll("((\\d+|5)[.、]\\s*是否解决)", addStr + " $1");
                            }
                            // 添加换行符
                            // cleanedRes1 = cleanedRes1.replaceAll("\\s*((\\d+|2)[.、]\\s*核查情况)","\n$1");
                            // cleanedRes1 = cleanedRes1.replaceAll("\\s*((\\d+|3)[.、]\\s*解决方案)",   "\n$1");
                            // cleanedRes1 = cleanedRes1.replaceAll("\\s*((\\d+|4)[.、]\\s*处理结果)",   "\n$1");
                            // cleanedRes1 = cleanedRes1.replaceAll("\\s*((\\d+|5)[.、]\\s*是否解决)",   "\n$1");
                            return cleanedRes1;
                        }
                        // 渠道2
                        else if (StringUtils.equals(PromptWordConstant.ChannelName2, dto.getChannel())) {
                            // 判断是否包含敏感词
                            if (sensitiveWordsFlag) {
                                cleanedRes1 = cleanedRes1.replaceAll("((\\d+|4)[.、]\\s*客户是否满意)", addStr + " $1");
                            }

                            cleanedRes1 = cleanedRes1.replaceAll("((\\d+|2)[.、]\\s*前期处理情况描述).*?(?=(\\d+|3)[.、]\\s*本次接单后处理情况)", "2.前期处理情况描述：无；");

                            cleanedRes1 = cleanedRes1.replaceAll("((\\d+|4)[.、]\\s*客户是否满意：.*)", "4.客户是否满意：是。");


                            // 添加换行符
                            // cleanedRes1 = cleanedRes1.replaceAll("\\s*((\\d+|2)[.、]\\s*前期处理情况描述)", "\n$1");
                            // cleanedRes1 = cleanedRes1.replaceAll("\\s*((\\d+|3)[.、]\\s*本次接单后处理情况)", "\n$1");
                            // cleanedRes1 = cleanedRes1.replaceAll("\\s*((\\d+|4)[.、]\\s*客户是否满意)", "\n$1");
                            return cleanedRes1;
                        }
                        // 渠道3
                        else if (StringUtils.equals(PromptWordConstant.ChannelName3, dto.getChannel())) {
                            // 添加换行符
                            cleanedRes1 = cleanedRes1.replaceAll("\\s*(?i)(b[.、]\\s*处理方案)", "\n$1");
                            cleanedRes1 = cleanedRes1.replaceAll("\\s*(?i)(c[.、]\\s*影响台席数)", "\n$1");
                            return cleanedRes1;
                        }
                        // 渠道4
                        else if (StringUtils.equalsAny(dto.getChannel(),
                                PromptWordConstant.ChannelName41,
                                PromptWordConstant.ChannelName42,
                                PromptWordConstant.ChannelName43,
                                PromptWordConstant.ChannelName44,
                                PromptWordConstant.ChannelName45,
                                PromptWordConstant.ChannelName46,
                                PromptWordConstant.ChannelName47
                        )) {
                            // 判断是否包含敏感词
                            if (sensitiveWordsFlag) {
                                cleanedRes1 = cleanedRes1.replaceAll("((\\d+|6)[.、]\\s*其他情况)", addStr + " $1");
                            }
                            cleanedRes1 = cleanedRes1.replaceAll("((\\d+|4)[.、]\\s*前期处理情况).*?(?=(\\d+|5)[.、]\\s*本次处理情况)", "4、前期处理情况：未通过我公司客服热线等渠道反映过；");
                            cleanedRes1 = cleanedRes1.replaceAll("((\\d+|6)[.、]\\s*其他情况：.*)", "6、其他情况：无。");
                            // 添加换行符
                            cleanedRes1 = cleanedRes1.replaceAll("\\s*((\\d+|2)[.、]\\s*是否于当地政府机构申诉)",  "\n$1");
                            cleanedRes1 = cleanedRes1.replaceAll("\\s*((\\d+|3)[.、]\\s*申诉人信息)", "\n$1");
                            cleanedRes1 = cleanedRes1.replaceAll("\\s*((\\d+|4)[.、]\\s*前期处理情况)","\n$1");
                            cleanedRes1 = cleanedRes1.replaceAll("\\s*((\\d+|5)[.、]\\s*本次处理情况)","\n$1");
                            cleanedRes1 = cleanedRes1.replaceAll("\\s*((\\d+|6)[.、]\\s*其他情况)","\n$1");
                            return cleanedRes1;
                        }
                        // 渠道5
                        else if (StringUtils.equals(PromptWordConstant.ChannelName5, dto.getChannel())) {
                            // 判断是否包含敏感词
                            if (sensitiveWordsFlag) {
                                cleanedRes1 = cleanedRes1 + addStr;
                            }

                            // 添加换行符
                            cleanedRes1 = cleanedRes1.replaceAll("\\s*(【联系时间】)", "\n$1");
                            cleanedRes1 = cleanedRes1.replaceAll("\\s*(【处理结果】)", "\n$1");

                            return cleanedRes1;
                        }
                        // 渠道6
                        else if (StringUtils.equals(PromptWordConstant.ChannelName6, dto.getChannel())) {
                            // 判断是否包含敏感词
                            if (sensitiveWordsFlag) {
                                cleanedRes1 = cleanedRes1.replaceAll("((\\d+|九)[.、]\\s*是否有相关业务协议)", addStr + " $1");
                            }
                            // 添加换行符
                            // cleanedRes1 = cleanedRes1.replaceAll("\\s*((\\d+|1)[.、]\\s*用户姓名)",  "\n$1");
                            cleanedRes1 = cleanedRes1.replaceAll("\\s*((\\d+|2)[.、]\\s*反映人)", "\n$1");
                            cleanedRes1 = cleanedRes1.replaceAll("\\s*((\\d+|3)[.、]\\s*投诉专业户)","\n$1");
                            cleanedRes1 = cleanedRes1.replaceAll("\\s*((\\d+|4)[.、]\\s*业务号码)","\n$1");
                            cleanedRes1 = cleanedRes1.replaceAll("\\s*((\\d+|5)[.、]\\s*争议或问题业务)","\n$1");
                            cleanedRes1 = cleanedRes1.replaceAll("\\s*((\\d+|6)[.、]\\s*本地投诉)","\n$1");
                            cleanedRes1 = cleanedRes1.replaceAll("\\s*((\\d+|7)[.、]\\s*用户问题是否解决)","\n$1");
                            cleanedRes1 = cleanedRes1.replaceAll("\\s*((\\d+|8)[.、]\\s*本单投诉处理方案及结果)","\n$1");
                            cleanedRes1 = cleanedRes1.replaceAll("\\s*((\\d+|9)[.、]\\s*是否有相关业务协议)","\n$1");
                            cleanedRes1 = cleanedRes1.replaceAll("\\s*((\\d+|10)[.、]\\s*用户是否认可)","\n$1");
                            cleanedRes1 = cleanedRes1.replaceAll("\\s*((\\d+|11)[.、]\\s*附件)","\n$1");
                            return cleanedRes1;
                        }
                    }
                    // 没有包含敏感词的渠道
                    else {
                        // 添加专项说明
                        cleanedRes1 = cleanedRes1 + " 5.是否解决: 问题已解决，用户认可处理结果。";

                        // 判断是否包含敏感词
                        if (sensitiveWordsFlag) {
                            cleanedRes1 = cleanedRes1.replaceAll("((\\d+|5)[.、]\\s*是否解决)", addStr + " $1");
                        }
                        // 添加换行符
                        // cleanedRes1 = cleanedRes1.replaceAll("\\s*((\\d+|2)[.、]\\s*核查情况)", "\n$1");
                        // cleanedRes1 = cleanedRes1.replaceAll("\\s*((\\d+|3)[.、]\\s*解决方案)", "\n$1");
                        // cleanedRes1 = cleanedRes1.replaceAll("\\s*((\\d+|4)[.、]\\s*处理结果)", "\n$1");
                        // cleanedRes1 = cleanedRes1.replaceAll("\\s*((\\d+|5)[.、]\\s*是否解决)", "\n$1");
                        return cleanedRes1;
                    }
                }
            } catch (Exception e) {
                logger.error("SSE格式内容解析失败", e);
                throw new CustomException("500", STATUS_CODE_500_MSG, 2);
            }
        }
        return cleanedRes;
    }

    /**
     * 工单总结接口服务
     * @param dto
     * @return
     */
    public JSONObject summaryReceipt(LLMRequestDto dto) {
        JSONObject responseObj = new JSONObject();
        JSONObject resultObj = new JSONObject();
        JSONObject dataObj = new JSONObject();

        try {
            LLMResDto llmResDto = sendEvent(dto);
            if (NonUtil.isNon(llmResDto)) {
                resultObj.put("errorCode", "200");
                resultObj.put("errorMessage", "返回内容为空");
                resultObj.put("status", 1);
            } else {
                resultObj.put("errorCode", "");
                resultObj.put("errorMessage", "");
                resultObj.put("status", 0);
                dataObj.put("summary", llmResDto.getData());
            }
        } catch (CustomException e) {
            resultObj.put("errorCode", String.valueOf(e.getErrorCode()));
            resultObj.put("errorMessage", e.getErrorMessage());
            resultObj.put("status", e.getStatus());
        } catch (Exception e) {
            resultObj.put("errorCode", "500");
            resultObj.put("errorMessage", STATUS_CODE_500_MSG);
            resultObj.put("status", 2);
        } finally {
            responseObj.put("data", dataObj);
            responseObj.put("result", resultObj);
        }
        return responseObj;
    }

}
